Skip to content

Skip ADO feed 403 'already contains' when re-publishing an existing version#11164

Merged
timotheeguerin merged 7 commits into
mainfrom
timotheeguerin/ado-feed-skip-already-contains
Jul 3, 2026
Merged

Skip ADO feed 403 'already contains' when re-publishing an existing version#11164
timotheeguerin merged 7 commits into
mainfrom
timotheeguerin/ado-feed-skip-already-contains

Conversation

@timotheeguerin

Copy link
Copy Markdown
Member

Problem

The ADO azure-sdk-for-js feed publish step (added in #11118) fails when a package version already exists in the feed:

npm http fetch PUT 403 .../@azure-tools%2fazure-http-specs
HttpErrorGeneral: 403 Forbidden - The feed 'azure-sdk-for-js' already contains file
'azure-http-specs-0.1.0-alpha.43-dev.0.tgz' in package '@azure-tools/azure-http-specs 0.1.0-alpha.43-dev.0'.

Azure DevOps Artifacts npm feeds are immutable: re-publishing an already-existing version returns an HTTP 403 with an already contains file ... in package ... message, not the npm 409 / EPUBLISHCONFLICT that the skip logic currently matches. As a result a harmless re-publish fails the pipeline.

There is no npm flag (--force, --skip-existing) that bypasses feed immutability, so the 403 must be tolerated explicitly.

Change

Extend the "already published, treat as no-op" detection in ado-feed-release.yml to also match the ADO immutability message (already contains file, already contains ... in package). The match is intentionally specific — a bare 403 is not matched — so genuine auth/permission 403s still fail the job.

Notes

  • eng/CI-only change — no changelog entry.
  • The same fix is being applied in microsoft/typespec-azure, which carries an identical copy of this template.

Azure DevOps npm feeds are immutable and reject re-publishing an existing version with an HTTP 403 'already contains file ... in package ...' message rather than the npm 409 / EPUBLISHCONFLICT. Match that specific phrase so a re-publish is treated as a no-op, while genuine auth/permission 403s still fail the job.
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

No changes needing a change description found.

@azure-sdk-automation

azure-sdk-automation Bot commented Jul 3, 2026

Copy link
Copy Markdown

You can try these changes here

🛝 Playground 🌐 Website 🛝 VSCode Extension

@timotheeguerin timotheeguerin marked this pull request as ready for review July 3, 2026 15:47
The feed also rejects a publish with a 403 'cannot be published to the feed because it exists in at least one of the feed's upstream sources' when the version is already available upstream (npmjs). Treat this as a no-op too.
The ADO 'pwsh' task runs with $ErrorActionPreference='Stop' and pwsh 7.4+ defaults $PSNativeCommandUseErrorActionPreference to $true, so a non-zero 'npm publish' exit (e.g. version already exists) threw a terminating error before the $LASTEXITCODE inspection, defeating the skip logic. Relax both so the skip check runs.
Replace the inline pwsh publish loop with a shared TypeScript script (publish-npm-packages.ts). A Node child process exposes the npm exit code directly, so the 'already exists' skip logic always runs (the pwsh version could throw before its LASTEXITCODE check). The deployment job now checks out sources and pins Node to run the script.
1ES release jobs forbid 'checkout', so the shared Node script approach cannot run in the publish deployment job. Go back to inline pwsh, but fix the real failure: after a skipped 'npm publish', the non-zero $LASTEXITCODE leaked as the pwsh process exit code and failed the task. Capture the exit code immediately, classify the output, and exit 0 explicitly when there is no genuine error. Also keep the EAP relaxation so npm's non-zero exit does not throw first.
The ADO PowerShell task appends 'exit $LASTEXITCODE' after the inline script (unless ignoreLASTEXITCODE is set). After a skipped 'npm publish' the leftover non-zero code fails the task even though we handled it. Reset $global:LASTEXITCODE explicitly (0 on success/skip, 1 on genuine error) in addition to our own exit, so both the appended line and our exit agree under either invocation scope.
Replace the inline pwsh publish with a shared, self-contained publish-npm-packages.ts run by node. It classifies each package as published/skipped/failed (treating ADO feed immutability and existing-upstream 403s as skippable no-ops), emits Azure DevOps log groups with colored status, prints a summary, and controls its own exit code so there is no leaked pwsh $LASTEXITCODE. 1ES release jobs forbid checkout, so the script is copied into the packages artifact at pack time and the release job downloads the whole artifact and runs it with a NodeTool-pinned Node.
@timotheeguerin timotheeguerin merged commit d9d9ab6 into main Jul 3, 2026
28 of 31 checks passed
@timotheeguerin timotheeguerin deleted the timotheeguerin/ado-feed-skip-already-contains branch July 3, 2026 18:33
timotheeguerin added a commit to Azure/typespec-azure that referenced this pull request Jul 3, 2026
…ersion (#4813)

## Problem

The ADO `azure-sdk-for-js` feed publish step (added in #4807) fails when
a package version already exists in the feed:

```
npm http fetch PUT 403 .../@Azure-Tools%2fazure-http-specs
HttpErrorGeneral: 403 Forbidden - The feed 'azure-sdk-for-js' already contains file
'azure-http-specs-0.1.0-alpha.43-dev.0.tgz' in package '@azure-tools/azure-http-specs 0.1.0-alpha.43-dev.0'.
```

Azure DevOps Artifacts npm feeds are **immutable**: re-publishing an
already-existing version returns an **HTTP 403** with an `already
contains file ... in package ...` message, not the npm `409` /
`EPUBLISHCONFLICT` that the skip logic currently matches. As a result a
harmless re-publish fails the pipeline.

There is no npm flag (`--force`, `--skip-existing`) that bypasses feed
immutability, so the 403 must be tolerated explicitly.

## Change

Extend the "already published, treat as no-op" detection in
`ado-feed-release.yml` to also match the ADO immutability message
(`already contains file`, `already contains ... in package`). The match
is intentionally **specific** — a bare `403` is **not** matched — so
genuine auth/permission 403s still fail the job.

## Notes

- eng/CI-only change — no changelog entry.
- The identical template lives in `microsoft/typespec` and receives the
same fix in microsoft/typespec#11164.

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant